﻿// Copyright (c) Inossem. All rights reserved.
// Licensed under the LGPL-3.0 license. See LICENSE file in the project root for full license information.
using DWF.Studio.ScriptEngine.Custom;
using DWF.Studio.ScriptEngine.Event;
using DWF.Studio.ScriptEngine.IPC.Msg;
using DWF.Studio.ScriptEngine.Proxy;
using DWF.Studio.Share.Log;
using DWF.Studio.Share.Serialization;
using System;
using System.Activities;
using System.Activities.Validation;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DWF.Studio.ScriptEngine
{
    /// <summary>
    /// 引擎代理上下文
    /// </summary>
    public class ScriptEngineProxyContext : IDisposable
    {
        /// <summary>
        /// 回响
        /// </summary>
        private string _echo = null;

        /// <summary>
        /// 等待回响
        /// </summary>
        private IThreadBlock _waitEchoBlock = null;

        /// <summary>
        /// 等待进程退出
        /// </summary>
        private IThreadBlock _waitExitBlock = null;

        /// <summary>
        /// ipc服务
        /// </summary>
        private IScriptEngineIPCService _engineIPCService = null;

        /// <summary>
        /// 引擎的进程
        /// </summary>
        private Process _engineProcess = null;

        /// <summary>
        /// 当前引擎
        /// </summary>
        public IScriptEngine CurrentEngine
        {
            get
            {
                _engineStack.TryPeek(out IScriptEngine engine);
                return engine;
            }
        }

        /// <summary>
        /// 管道名称
        /// </summary>
        public string PipeName
        {
            get => _engineIPCService.GetPipeName();
        }

        /// <summary>
        /// 加载子引擎
        /// </summary>
        public Action<IScriptEngine, EEngineType, string> LoadSubEngine = null;

        /// <summary>
        /// 卸载子引擎
        /// </summary>
        public Action<string, string> UnloadSubEngine = null;

        /// <summary>
        /// 执行引擎栈
        /// </summary>
        private ConcurrentStack<IScriptEngine> _engineStack = null;

        public StartupProperties _startupProps { get; private set; }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="engineIPCService"></param>
        public ScriptEngineProxyContext(IScriptEngineIPCService engineIPCService)
        {
            _engineStack = new ConcurrentStack<IScriptEngine>();
            _engineIPCService = engineIPCService;
            _engineIPCService.AddReceiveAction(receiveMsg);
            _waitEchoBlock = new ManualThreadBlock();
            _waitExitBlock = new AutoThreadBlock();
        }

        public void SetDebug(bool isDebugByStep)
        {
            var currentEngine = CurrentEngine;

            if (currentEngine is WorkflowScriptEngineProxy)
            {
                SendMsg<bool>(isDebugByStep, EMsgType.DebugStatus, currentEngine.Id, EEngineType.WorkflowScriptEngine);
            }
        }

        public void RefreshBreakpoints(string scriptPath)
        {
            SendMsg<string>(scriptPath, EMsgType.RefreshBreakpoints, string.Empty, EEngineType.Ignore);
        }

        /// <summary>
        /// 收到消息
        /// </summary>
        /// <param name="msg"></param>
        private void receiveMsg(UpwardMsg msg)
        {
            if (msg.MsgType == EMsgType.Echo)//回响消息
            {
                _echo = msg.GetDatum<string>();
                if (_echo != null)
                {
                    _waitEchoBlock.Continue();
                }
            }
            else if (msg.MsgType == EMsgType.ExcuteEnd)//执行结束
            {
                SendMsg<string>(String.Empty, EMsgType.Close, String.Empty, EEngineType.Ignore);
            }
            else if (_engineStack.TryPeek(out IScriptEngine engine) && engine.Id == msg.EngineId)//返回栈顶的引擎
            {
                if (msg.MsgType == EMsgType.ExcuteAborted)
                {
                    var datum = msg.GetDatum<ExecuteAbortedEventArgs>();
                    if (datum != null)
                    {
                        engine.ExecuteAborted?.Invoke(datum);
                    }
                }
                else if (msg.MsgType == EMsgType.ExcuteCompleted)
                {
                    var datum = msg.GetDatum<ExecuteCompletedEventArgs>();
                    if (datum != null)
                    {
                        engine.ExecuteCompleted?.Invoke(datum);
                    }
                }
                else if (msg.MsgType == EMsgType.ExcuteUnhandledException)
                {
                    var datum = msg.GetDatum<ExecuteUnhandledExceptionEventArgs>();
                    if (datum != null)
                    {
                        engine.ExecuteOnUnhandledException?.Invoke(datum);
                    }
                }
                else if (msg.MsgType == EMsgType.ExcuteTrack)
                {
                    //判断配置，是否需要传输运行日志消息，如不需要，记录到文件
                    var datum = msg.GetDatum<ExecuteTrackEventArgs>();
                    if (datum != null)
                    {
                        engine.ExecuteTrack?.Invoke(datum);
                    }
                }
                else if (msg.MsgType == EMsgType.DebugTrack)
                {
                    var datum = msg.GetDatum<DebugTrackEventArgs>();
                    if (datum != null)
                    {
                        engine.DebugTrack?.Invoke(datum);
                    }
                }
                else if (msg.MsgType == EMsgType.HitBreakpoint)
                {
                    var datum = msg.GetDatum<HitBreakpointEventArgs>();
                    if (datum != null)
                    {
                        engine.HitBreakpoint?.Invoke(datum);
                    }
                }
                else if (msg.MsgType == EMsgType.ExcuteStart)
                {
                    var datum = msg.GetDatum<ExecuteStartEventArgs>();
                    engine.ExecuteStart?.Invoke(datum);
                }
                else if (msg.MsgType == EMsgType.UnloadSubEngine)//卸载子引擎
                {
                    Pop(engine);//弹出当前引擎
                    if (UnloadSubEngine != null)
                    {
                        UnloadSubEngine(CurrentEngine.Id, engine.Id);//调用事件
                        //Oliver add 2021-06-21 子引擎卸载后，将子引擎id回传远程端，用以记录日志
                        SendMsg<string>(string.Empty, EMsgType.UnloadSubEngineEcho, engine.Id, EEngineType.Ignore);
                    }
                }
                else if (msg.MsgType == EMsgType.LoadSubEngine)//加载子引擎
                {
                    var eventArgs = msg.GetDatum<LoadSubEngineEventArgs>();

                    IScriptEngine subEngine = null;
                    switch (eventArgs.SubEngineType)//创建引擎代理
                    {
                        case EEngineType.WorkflowScriptEngine: subEngine = new WorkflowScriptEngineProxy(eventArgs.SubEngineId, this); break;
                    }

                    if (subEngine != null && LoadSubEngine != null)
                    {
                        LoadSubEngine(subEngine, eventArgs.SubEngineType, eventArgs.ScriptPath);
                        SendMsg<string>(String.Empty, EMsgType.LoadedSubEngine, eventArgs.SubEngineId, eventArgs.SubEngineType);//通知引擎进程 引擎代理加载完成
                    }
                }
            }
        }

        /// <summary>
        /// 压入引擎
        /// </summary>
        /// <param name="scriptEngine"></param>
        internal void Push(IScriptEngine scriptEngine)
        {
            _engineStack.Push(scriptEngine);
        }

        /// <summary>
        /// 弹出引擎
        /// </summary>
        /// <param name="scriptEngine"></param>
        internal void Pop(IScriptEngine scriptEngine)
        {
            if (_engineStack.TryPop(out IScriptEngine head) && head != scriptEngine)
            {
                throw new EngineException();
            }

            scriptEngine?.Dispose();
        }

        /// <summary>
        /// 等待回响
        /// </summary>
        /// <returns></returns>
        internal string WaitEcho()
        {
            _waitEchoBlock.Reset();
            if (!_waitEchoBlock.Block(TimeSpan.FromSeconds(1000)))
            {
                throw new EngineException();
            }

            return _echo;
        }

        /// <summary>
        /// 卸载引擎进程
        /// </summary>
        /// <param name="outtime"></param>
        internal void UnloadEngineProcess(int outtime)
        {
            if (_engineProcess != null)
            {
                var isnormal = _waitExitBlock.Block(outtime);
                if (!isnormal)
                {
                    try
                    {
                        _engineProcess?.Kill();
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Error(ex);
                    }
                }
            }
        }

        /// <summary>
        /// 设置引擎进程，添加启动进程传递的记录日志所需参数 Oliver add 2021-06-24
        /// </summary>
        /// <param name="fullname"></param>
        /// <param name="arguments"></param>
        /// <param name="projectProperties"></param>
        internal void StartEngineProcess(string fullname, string arguments, Dictionary<string, string> projectProperties)
        {
            this._startupProps = new StartupProperties(projectProperties);
            this.StartEngineProcess(fullname, arguments);
        }
        /// <summary>
        /// 设置引擎进程
        /// </summary>
        /// <param name="engineProcess"></param>
        internal void StartEngineProcess(string fullname, string arguments)
        {
            _engineProcess = new Process();
            _engineProcess.StartInfo.FileName = fullname;
            _engineProcess.StartInfo.Arguments = arguments;
            _engineProcess.StartInfo.RedirectStandardInput = true;
            _engineProcess.StartInfo.RedirectStandardOutput = true;
            _engineProcess.StartInfo.RedirectStandardError = true;
            _engineProcess.StartInfo.CreateNoWindow = true;
            _engineProcess.StartInfo.UseShellExecute = false; ;
            _engineProcess.EnableRaisingEvents = true;
            _engineProcess.Exited += (sender, e) =>
            {
                _waitExitBlock.Continue();
                CurrentEngine?.ExecuteEnd?.Invoke(EventArgs.Empty);
                _engineStack.Clear();
                _engineProcess = null;
            };

            _engineProcess.OutputDataReceived += (sender, e) =>
            {
                if (!String.IsNullOrWhiteSpace(e.Data))
                {
                    if (_startupProps.IsSilentExecution)  //静默执行，不输出 Oliver add 2021-06-25
                        return;

                    if (!_startupProps.IsConsoleOutToFile)
                        Console.WriteLine(e.Data);
                    else
                    {
                        var msg = $"[{ DateTime.Now:T}] {e.Data.Replace(System.Environment.NewLine, "")}";
                        LogHelper.Logger.LogMsg(
                            _startupProps.projectName
                            , _startupProps.scriptFileName_WithoutExtension
                            , msg
                            , "_ConsoleOut"
                        );
                    }
                }
            };
            _engineProcess.ErrorDataReceived += (sender, e) =>
            {
                if (!String.IsNullOrWhiteSpace(e.Data))
                {
                    if (_startupProps.IsSilentExecution)  //静默执行，不输出 Oliver add 2021-06-25
                        return;

                    if (!_startupProps.IsConsoleOutToFile)
                        Console.WriteLine(e.Data);
                    else
                    {
                        var msg = $"[{ DateTime.Now:T}] {e.Data.Replace(System.Environment.NewLine, "")}";
                        LogHelper.Logger.LogMsg(
                            _startupProps.projectName
                            , _startupProps.scriptFileName_WithoutExtension
                            , msg
                            , "_ConsoleOut"
                        );
                    }
                }
            };
            _engineProcess.Start();

            _engineProcess.BeginErrorReadLine();
            _engineProcess.BeginOutputReadLine();

        }

        /// <summary>
        /// 发送消息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="datum"></param>
        /// <param name="msgType"></param>
        /// <param name="engineType"></param>
        internal void SendMsg<T>(T datum, EMsgType msgType, string engineId, EEngineType engineType)
        {
            DownwardMsg downwardMsg = new DownwardMsg();
            downwardMsg.EngineId = engineId;
            downwardMsg.EngineType = engineType;
            downwardMsg.MsgType = msgType;
            downwardMsg.SetDatum(datum);
            _engineIPCService.Send(downwardMsg);
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    _engineIPCService.RemoveReceiveAction(receiveMsg);
                    _waitExitBlock.Dispose();
                    _waitEchoBlock.Dispose();
                    _startupProps.Dispose();  //Oliver add 2021-06-25
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。

                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~ExcutionContext()
        // {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion
    }
}
